﻿namespace FSharpChess
open System.Windows.Forms
open System.IO
open System.Runtime.Serialization.Formatters.Binary
open System
open Microsoft.FSharp.Collections

module Opn =
    let (|Nd|Lf|) (node : TreeNode) =
         if node.Nodes.Count>0 then
             Nd (node.Text, [for x in node.Nodes -> x ])
         else
             Lf (node.Text)    
    let fn = Path.GetDirectoryName(Application.ExecutablePath)+"\Openings.ser"
    let fntxt = Path.GetDirectoryName(Application.ExecutablePath)+"\Openings.txt"
    let mutable psns = new OpHash()
    let lnFromTreeNode (nd:TreeNode) =
        match nd with
            |Nd (s,_) -> s
            |Lf x -> x
    let GetPsnsFromNode (nd:TreeNode) =
        let ans = new ResizeArray<string>()
        let rec GetTagsFromNodeInner n =
            match n with
                |Nd (s,ndl) -> 
                    ans.Add(nd.Tag.ToString())
                    List.iter GetTagsFromNodeInner ndl
                |Lf x -> ()
        GetTagsFromNodeInner nd
        ResizeArray.toList ans
    let DeletePsns (pl:string list) =
        let delpsn p = if psns.ContainsKey(p) then psns.Remove(p) |> ignore
        List.iter delpsn pl
    let MakeBest (fen:string,bmv:string) =
        let mvs = psns.[fen]
        let rec newlst ol nl =
            match ol with
                |[] -> List.rev nl
                |_ ->
                    if ol.Head=bmv then newlst ol.Tail nl else newlst ol.Tail (ol.Head::nl)
        let newl = bmv::newlst mvs []
        psns.Remove(fen)|>ignore
        psns.Add(fen,newl)           
    let addvrs (gmpsns:(string * string) list) =
        let addit gmpsn =
            let g,m = gmpsn
            let b,vrs = psns.TryGetValue(g)
            if b then
                if not (List.contains m vrs) then 
                    psns.Remove(g)|>ignore
                    psns.Add(g,vrs@[m])
            else
                psns.Add(g,m::[])   
        List.iter addit gmpsns    
    let mutable inOpening = false
    let toFile () =
        let formatter = new BinaryFormatter()
        let fsOut = new FileStream(fn,FileMode.Create)
        formatter.Serialize(fsOut,box psns)
        fsOut.Close()
    let toFileTxt () =
        let mutable outstr = ""
        let convlist sl = List.fold (fun s t -> s+"\""+t+"\";") "" sl
        for ohe in psns do
            outstr <- outstr + "oh.Add(\"" + ohe.Key + "\",[" + convlist ohe.Value + "])" + Environment.NewLine
        File.WriteAllText(fntxt,outstr)
    let fromFile (): OpHash =
        let formatter = new BinaryFormatter()
        let fsIn = new FileStream(fn,FileMode.Open)
        let res = formatter.Deserialize(fsIn)
        fsIn.Close()
        unbox res
    let ohdefault =
        let oh=new OpHash()
        oh.Add("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",["d4";"e4";"c4";"Nc3";])
        oh.Add("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1",["e6";])
        oh.Add("rnbqkbnr/ppp2ppp/4p3/3p4/3PP3/8/PPP2PPP/RNBQKBNR w KQkq d6 0 3",["Nd2";"Nc3";"e5";])
        oh.Add("rnbqkbnr/pppp1ppp/4p3/8/3PP3/8/PPP2PPP/RNBQKBNR b KQkq d3 0 2",["d5";])
        oh.Add("rnbqkbnr/pppp1ppp/4p3/8/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2",["d4";])
        oh.Add("rnbqkbnr/pppp1ppp/4p3/8/3PP3/8/PPP2PPP/RNBQKBNR b KQkq e3 0 2",["d5";])
        oh.Add("rnbqkbnr/pppp1ppp/4p3/8/3P4/8/PPP1PPPP/RNBQKBNR w KQkq - 0 2",["e4";])
        oh.Add("rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq d3 0 1",["e6";])
        oh.Add("rnbqkbnr/ppp2ppp/4p3/3p4/3PP3/8/PPPN1PPP/R1BQKBNR b KQkq - 1 3",["Nc6";])
        oh.Add("rnbqkbnr/ppp2ppp/4p3/3pP3/3P4/8/PPP2PPP/RNBQKBNR b KQkq - 0 3",["c5";])
        oh.Add("rnbqkbnr/pp3ppp/4p3/2ppP3/3P4/2P5/PP3PPP/RNBQKBNR b KQkq - 0 4",["Qb6";])
        oh.Add("rnbqkbnr/pp3ppp/4p3/2ppP3/3P4/8/PPP2PPP/RNBQKBNR w KQkq c6 0 4",["c3";])
        oh.Add("rnb1kbnr/pp3ppp/1q2p3/2ppP3/3P4/2P2N2/PP3PPP/RNBQKB1R b KQkq - 2 5",["Nc6";])
        oh.Add("rnb1kbnr/pp3ppp/1q2p3/2ppP3/3P4/2P5/PP3PPP/RNBQKBNR w KQkq - 1 5",["Nf3";])
        oh.Add("r1bqkbnr/ppp2ppp/2n1p3/3p4/3PP3/8/PPPN1PPP/R1BQKBNR w KQkq - 2 4",["Ngf3";])
        oh.Add("r1bqkbnr/ppp2ppp/2n1p3/3p4/3PP3/5N2/PPPN1PPP/R1BQKB1R b KQkq - 3 4",["Nf6";])
        oh.Add("r1bqkb1r/1ppn1ppp/2n1p3/p2pP3/P2P4/1N3N2/1PP2PPP/R1BQKB1R b KQkq a3 0 7",["b6";])
        oh.Add("r1bqkb1r/1ppn1ppp/2n1p3/p2pP3/3P4/1N3N2/PPP2PPP/R1BQKB1R w KQkq a6 0 7",["a4";])
        oh.Add("r1bqkb1r/pppn1ppp/2n1p3/3pP3/3P4/1N3N2/PPP2PPP/R1BQKB1R b KQkq - 2 6",["a5";])
        oh.Add("r1bqkb1r/pppn1ppp/2n1p3/3pP3/3P4/5N2/PPPN1PPP/R1BQKB1R w KQkq - 1 6",["Nb3";])
        oh.Add("r1bqkb1r/ppp2ppp/2n1pn2/3pP3/3P4/5N2/PPPN1PPP/R1BQKB1R b KQkq - 0 5",["Nd7";])
        oh.Add("r1bqkb1r/ppp2ppp/2n1pn2/3p4/3PP3/5N2/PPPN1PPP/R1BQKB1R w KQkq - 4 5",["e5";])
        oh.Add("rnbqk1nr/pp3ppp/4p3/2ppP3/1b1P4/2N5/PPP2PPP/R1BQKBNR w KQkq c6 0 5",["a3";])
        oh.Add("rnbqk1nr/ppp2ppp/4p3/3pP3/1b1P4/2N5/PPP2PPP/R1BQKBNR b KQkq - 0 4",["c5";])
        oh.Add("rnbqk1nr/ppp2ppp/4p3/3p4/1b1PP3/2N5/PPP2PPP/R1BQKBNR w KQkq - 2 4",["e5";])
        oh.Add("rnbqkbnr/ppp2ppp/4p3/3p4/3PP3/2N5/PPP2PPP/R1BQKBNR b KQkq - 1 3",["Bb4";])
        oh.Add("rnbqk1nr/ppp2ppp/4p3/3p4/1b1PP3/2N5/PPP2PPP/R1BQKBNR w KQkq - 1 4",["e5";])
        oh.Add("rnbqkbnr/ppp2ppp/4p3/3p4/3PP3/2N5/PPP2PPP/R1BQKBNR b KQkq e3 0 3",["Bb4";])
        oh.Add("rnbqkbnr/ppp2ppp/4p3/3p4/3P4/2N5/PPP1PPPP/R1BQKBNR w KQkq - 0 3",["e4";])
        oh.Add("rnbqkbnr/ppp1pppp/8/3p4/3P4/2N5/PPP1PPPP/R1BQKBNR b KQkq d3 0 2",["e6";])
        oh.Add("rnbqkbnr/ppp1pppp/8/3p4/8/2N5/PPPPPPPP/R1BQKBNR w KQkq d6 0 2",["d4";])
        oh.Add("rnbqkbnr/pppppppp/8/8/8/2N5/PPPPPPPP/R1BQKBNR b KQkq - 1 1",["d5";])
        oh.Add("rnbqk1nr/pp3ppp/4p3/2ppP3/3P4/P1b5/1PP2PPP/R1BQKBNR w KQkq - 0 6",["bxc3";])
        oh.Add("rnbqk1nr/pp3ppp/4p3/2ppP3/1b1P4/P1N5/1PP2PPP/R1BQKBNR b KQkq - 0 5",["Bxc3+";])
        oh
    let load() =
        inOpening <- true
        psns <- if File.Exists(fn) then fromFile() else ohdefault
    let getmv(psn:Posn) =
        let b,vrs = psns.TryGetValue(psn.ToFen())
        if b then vrs.[0] else ""
